﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HAP = HtmlAgilityPack;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;


namespace StockSBIE
{
    public class TSE_SB_Crawling
    {
        HAP.HtmlWeb web = null;
        protected HAP.HtmlWeb getWeb()
        {
            if (web == null)
                web = new HAP.HtmlWeb();
            return web;
        }
        protected String getURL(String code, int count)
        {
            int c = count / 100 + 1;
            String s = String.Format("http://bsr.twse.com.tw/bshtm/bshtm_report_Messages.aspx?StartNumber={0}&FocusIndex=All_{1}&flg_Print=0", code, c);
            return s;
        }
        /*************************************************************
         * 
         * 
         **************************************************************/


        protected void processInst(String data, out String code, out String inst)
        {
            int pos = data.IndexOf(" ");
            if (pos > 0)
            {
                String s1 = data.Substring(0, pos).Trim();
                String s2 = data.Substring(pos).Trim();
                code = s1;
                inst = s2.Replace("　", "").Replace(" ", "");

            }
            else
            {
                code = data;
                inst = "";
            }
        }

        protected SBEntry processRow(HAP.HtmlNodeCollection tds)
        {
            if (tds == null)
                return null;
            if (tds.Count != 5)
                return null;
            String seqstr = tds[0].InnerText;
            String inststr = tds[1].InnerText;
            String price_str = tds[2].InnerText;
            String buy_str = tds[3].InnerText;
            String sell_str = tds[4].InnerText;

            seqstr = seqstr.Replace("&nbsp;", "").Trim();
            inststr = inststr.Replace("&nbsp;", " ").Trim();
            price_str = price_str.Replace("&nbsp;", "").Trim();
            buy_str = buy_str.Replace("&nbsp;", "").Trim();
            sell_str = sell_str.Replace("&nbsp;", "").Trim();

            if (String.IsNullOrWhiteSpace(seqstr))
                return null;

            if (String.IsNullOrWhiteSpace(inststr))
                return null;

            if (String.IsNullOrWhiteSpace(price_str))
                return null;

            int seq = 0;
            if (int.TryParse(seqstr, out seq) == false)
            {
                return null;
            }

            String instCode = "";
            String instName = "";
            processInst(inststr, out instCode, out instName);



            SBEntry entry = new SBEntry();
            entry.seq = seq;
            entry.instCode = instCode;
            entry.instName = instName;

            decimal price = new decimal(0);
            if (decimal.TryParse(price_str, out  price))
            {
                entry.price = price;
            }

            decimal buy = new decimal(0);
            decimal sell = new decimal(0);

            if (decimal.TryParse(buy_str, out buy))
            {
                entry.buy = Convert.ToInt32(buy);
            }

            if (decimal.TryParse(sell_str, out sell))
            {
                entry.sell = Convert.ToInt32(sell);
            }

            return entry;
        }

        public class SBComparer : IComparer<SBEntry>
        {
            public int Compare(SBEntry x, SBEntry y)
            {
                return x.seq - y.seq;
            }
        }


        public SBRecords CrawlingSB(String code, DateTime d, int count)
        {
            String url = getURL(code, count);

            try
            {
                HAP.HtmlDocument doc = getWeb().Load(url);
                HAP.HtmlNodeCollection price_3s = doc.DocumentNode.SelectNodes("//tr[@class='column_value_price_3']");
                HAP.HtmlNodeCollection price_2s = doc.DocumentNode.SelectNodes("//tr[@class='column_value_price_2']");

                List<SBEntry> entries = new List<SBEntry>();
                foreach (HAP.HtmlNode price_3 in price_3s)
                {
                    HAP.HtmlNodeCollection tds = price_3.SelectNodes(price_3.XPath + "/td");
                    SBEntry entry = processRow(tds);
                    if (entry != null)
                    {
                        entries.Add(entry);
                    }
                }
                foreach (HAP.HtmlNode price_2 in price_2s)
                {
                    HAP.HtmlNodeCollection tds = price_2.SelectNodes(price_2.XPath + "/td");
                    SBEntry entry = processRow(tds);
                    if (entry != null)
                    {
                        entries.Add(entry);
                    }
                }
                //根據seq sort        
                entries.Sort(new SBComparer());

                SBRecords result = new SBRecords();
                result.SBEntries = entries;
                return result;

            }
            catch (Exception e)
            {
                return null;
            }

        }

        private Comparison<SBEntry> IComparer<T1>()
        {
            throw new NotImplementedException();
        }

    }
    [Serializable()]
    public class SBEntry : ISerializable
    {
        public int seq;
        public String instCode;
        public String instName;
        public decimal price;
        public int buy;
        public int sell;
        public Guid instId = Guid.Empty;

        public SBEntry()
        {
        }
        public SBEntry(SerializationInfo info, StreamingContext ctxt)
        {
            this.seq = (int)info.GetValue("seq", typeof(int));
            this.instCode = (String)info.GetValue("instCode", typeof(String));
            this.instName = (String)info.GetValue("instName", typeof(String));
            this.price = (decimal)info.GetValue("price", typeof(decimal));
            this.buy = (int)info.GetValue("buy", typeof(int));
            this.sell = (int)info.GetValue("sell", typeof(int));
            this.instId = (Guid)info.GetValue("instId", typeof(Guid));
        }
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("seq", this.seq);
            info.AddValue("instCode", this.instCode);
            info.AddValue("instName", this.instName);
            info.AddValue("price", this.price);
            info.AddValue("buy", this.buy);
            info.AddValue("sell", this.sell);
            info.AddValue("instId", this.instId);
        }
    }

    [Serializable()]
    public class SBRecords : ISerializable
    {
        public List<SBEntry> SBEntries;
        public SBRecords()
        {
        }
        public SBRecords(SerializationInfo info, StreamingContext ctxt)
        {
            this.SBEntries = (List<SBEntry>)info.GetValue("entries", typeof(List<SBEntry>));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("entries", this.SBEntries);
        }
        public static SBRecords LoadFromFile(String fileName)
        {
            SBRecords result;
            Stream stream = File.Open(fileName, FileMode.Open);
            BinaryFormatter bFormatter = new BinaryFormatter();
            result = (SBRecords)bFormatter.Deserialize(stream);
            stream.Close();
            return result;

        }
        public void SaveToFile(String fileName)
        {
            Stream stream = File.Open(fileName, FileMode.Create);
            BinaryFormatter bFormatter = new BinaryFormatter();
            bFormatter.Serialize(stream, this);
            stream.Close();
        }
    }

}
